/**
 * precedencepropagator.cpp is part of %PROJECTNAME%.
 *
 * %PROJECTNAME% is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * %PROJECTNAME% is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with %PROJECTNAME%.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "precedencepropagator.h"

///////////////////////////////////////////////////////////////
PrecedencePropagator::PrecedencePropagator( Gecode::Space & a_home, Gecode::Int::IntView a_end, Gecode::Int::IntView a_start ) :
    Gecode::Propagator( a_home ),
    m_end( a_end ),
    m_start( a_start )
{
    m_end.subscribe( a_home, *this, Gecode::Int::PC_INT_DOM );
    m_start.subscribe( a_home, *this, Gecode::Int::PC_INT_DOM );
}

///////////////////////////////////////////////////////////////
PrecedencePropagator::PrecedencePropagator( Gecode::Space & a_home, bool a_share, PrecedencePropagator & a_other ) :
    Propagator( a_home, a_share, a_other )
{
    m_end.update( a_home, a_share, a_other.m_end );
    m_start.update( a_home, a_share, a_other.m_start );
}

///////////////////////////////////////////////////////////////
PrecedencePropagator::~PrecedencePropagator()
{
}

///////////////////////////////////////////////////////////////
size_t PrecedencePropagator::dispose( Gecode::Space & a_home )
{
    m_end.cancel( a_home, *this, Gecode::Int::PC_INT_DOM);
    m_start.cancel( a_home, *this, Gecode::Int::PC_INT_DOM);
    Gecode::Propagator::dispose( a_home );

    return sizeof( *this );
}

///////////////////////////////////////////////////////////////
PrecedencePropagator * PrecedencePropagator::copy( Gecode::Space & a_home, bool a_share )
{
    return new ( a_home ) PrecedencePropagator( a_home, a_share, *this );
}

///////////////////////////////////////////////////////////////
Gecode::PropCost PrecedencePropagator::cost( const Gecode::Space & a_home, const Gecode::ModEventDelta & a_delta ) const
{
    return Gecode::PropCost::binary( Gecode::PropCost::LO );
}

///////////////////////////////////////////////////////////////
Gecode::ExecStatus PrecedencePropagator::propagate( Gecode::Space & a_home, const Gecode::ModEventDelta & a_delta )
{
    GECODE_ME_CHECK( m_end.le( a_home, m_start.min() ) );
    GECODE_ME_CHECK( m_start.gq( a_home, m_end.max() ) );

    if( m_end.max() <= m_start.min() )
        return a_home.ES_SUBSUMED( *this );
    else
        return Gecode::ES_FIX;
}

///////////////////////////////////////////////////////////////
Gecode::ExecStatus PrecedencePropagator::post( Gecode::Space & a_home, Gecode::Int::IntView a_end, Gecode::Int::IntView a_start )
{
    if( same( a_end, a_start ) )
        return Gecode::ES_FAILED;

    GECODE_ME_CHECK( a_end.le( a_home, a_start.min() ) );
    GECODE_ME_CHECK( a_start.gq( a_home, a_end.max() ) );

    if( a_end.max() > a_start.min() )
        ( void ) new ( a_home ) PrecedencePropagator( a_home, a_end, a_start );

    return Gecode::ES_OK;
}

///////////////////////////////////////////////////////////////
void precedence( Gecode::Space & home, Gecode::IntVar a_end, Gecode::IntVar a_start )
{
    if( !home.failed() )
        GECODE_ES_FAIL( PrecedencePropagator::post( home, a_end, a_start ) );
}
